package com.ml.loan.common.lock;

import com.ml.loan.common.constants.CommonConstant;
import com.ml.loan.common.exception.BusinessException;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * redisson分布式锁的实现，基本锁功能的抽象实现
 * 如果有其他方式（如zookeeper）实现的分布式锁，可通过在配置文件zsk.lock.lockType=xxx切换
 *
 * @author czx
 */
@Component
@ConditionalOnClass(RedissonClient.class)
@ConditionalOnProperty(prefix = "zsk.lock", name = "lockType", havingValue = "redis", /* 该配置不存在正常加载该Bean */ matchIfMissing = true)
public class RedissonDistributedLock implements DistributedLock {

    @Autowired
    private RedissonClient redissonClient;

    private ZLock getLock(String key, boolean isFair) {
        RLock lock;
        if (isFair) {
            lock = redissonClient.getFairLock(getRedissonKey(key));
        } else {
            lock = redissonClient.getLock(getRedissonKey(key));
        }
        return new ZLock(lock, this);
    }

    @Override
    public ZLock lock(String key, long leaseTime, TimeUnit unit, boolean isFair) throws Exception {
        leaseTime = Optional.ofNullable(leaseTime).orElse(10L);
        unit = Optional.ofNullable(unit).orElse(TimeUnit.SECONDS);
        ZLock zLock = getLock(key, isFair);
        RLock lock = (RLock) zLock.getLock();
        lock.lock(leaseTime, unit);
        return zLock;
    }

    @Override
    public ZLock tryLock(String key, long waitTime, long leaseTime, TimeUnit unit, boolean isFair) throws Exception {
        leaseTime = Optional.ofNullable(leaseTime).orElse(10L);
        unit = Optional.ofNullable(unit).orElse(TimeUnit.SECONDS);
        ZLock zLock = getLock(key, isFair);
        RLock lock = (RLock) zLock.getLock();
        if (lock.tryLock(waitTime, leaseTime, unit)) {
            return zLock;
        }
        return null;
    }

    /**
     * 解锁
     *
     * @param lock
     * @throws Exception
     */
    @Override
    public void unlock(Object lock) throws Exception {
        if (lock != null && lock instanceof RLock) {
            RLock rLock = (RLock) lock;
            if (rLock.isLocked() && rLock.isHeldByCurrentThread()) {
                //被锁定并且被当前线程保持时解锁
                rLock.unlock();
            }
        } else {
            throw new BusinessException("requires RLock type");
        }
    }

    /**
     * redission锁前缀
     *
     * @param key
     * @return
     */
    private String getRedissonKey(String key) {
        return CommonConstant.REDISSON_PREFIX + CommonConstant.COLON + key;
    }
}
